package com.soviet.boot.common.aspect;

import com.alibaba.fastjson.JSONObject;
import com.soviet.boot.common.exception.ServiceException;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


@Aspect
@Slf4j
@Component
public class HttpRequestAspect {

    @Pointcut("@within(com.soviet.boot.common.annotation.ResultAndLog) || " +
            "@annotation(com.soviet.boot.common.annotation.ResultAndLog)")
    public void log() {

    }

    @Around("log()")
    public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {

        //记录开始时间
        long startTime = System.currentTimeMillis();

        //方法签名
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();
        Object[] paramsArgsValue = proceedingJoinPoint.getArgs();
        String[] parameterNames = methodSignature.getParameterNames();
        JSONObject paramJson = logParam(parameterNames, proceedingJoinPoint.getArgs());

        if (log.isDebugEnabled()) {
            log.debug("[beforeMethodAdvice]URL:{}", request.getRequestURL().toString());
            log.debug("[beforeMethodAdvice]HTTP_METHOD:{}", request.getMethod());
            log.debug("[beforeMethodAdvice]IP:{} ", request.getRemoteAddr());
            log.debug("[beforeMethodAdvice]CLASS_METHOD : {}.{}", proceedingJoinPoint.getSignature().getDeclaringTypeName(), methodSignature.getName());
            log.debug("[beforeMethodAdvice]ARGS:{}", paramJson);
        }


        if (paramsArgsValue.length > 0) {
            for (Object arg : paramsArgsValue) {
                if (arg instanceof BindingResult) {
                    BindingResult result = (BindingResult) arg;
                    if (result.hasErrors()) {
                        String errorInfo = result.getFieldError().getDefaultMessage();
                        log.debug("[beforeMethodAdvice]参数校验不合法!{}", errorInfo);
                        throw new ServiceException(errorInfo);
                    }
                }
            }
        }

        Object ob;
        ob = proceedingJoinPoint.proceed();
        long time = System.currentTimeMillis() - startTime;

        if (log.isDebugEnabled()) {
            log.debug("[afterReturningAdvice]spend time:{}", time + "ms");
            log.debug("[afterReturningAdvice]响应结果:{}", JSONObject.toJSONString(ob));
        }

        return ob;
    }


    /**
     * 拼接请求参数
     *
     * @param paramsArgsName  参数名称
     * @param paramsArgsValue 参数值
     * @return key-value形式 key:参数名 value:参数接收到的值
     */
    private JSONObject logParam(String[] paramsArgsName, Object[] paramsArgsValue) {
        JSONObject jsonObject = null;
        try {

            if (paramsArgsName.length < 1 || paramsArgsValue.length < 1) {
                return jsonObject;
            }
            jsonObject = new JSONObject();
            for (int i = 0; i < paramsArgsValue.length; i++) {
                //参数名
                String name = paramsArgsName[i];
                //参数值
                Object value = paramsArgsValue[i];
                if (value instanceof HttpServletRequest) {
                    //不输出HttpServletRequest 参数信息
                    continue;
                } else if (value instanceof BindingResult) {
                    //不输出HttpServletRequest 参数信息
                    continue;
                } else if (value instanceof HttpServletResponse) {
                    continue;
                }
                jsonObject.put(name, value);
            }
        } catch (Exception e) {
            log.debug("[beforeMethodAdvice Exception]:请求参数拼接为JSON对象出现异常:", e);
        }

        return jsonObject;
    }

}
